using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Security.Cryptography;

namespace Beauty.Net.JsonWebToken.Core;

/// <summary>
/// JWT服务实现
/// </summary>
public class JwtService : IJwtService
{
    private readonly JwtOptions _options;
    private readonly JwtSecurityTokenHandler _tokenHandler;
    private readonly SigningCredentials _signingCredentials;
    
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="options">JWT配置选项</param>
    public JwtService(IOptions<JwtOptions> options)
    {
        _options = options.Value ?? throw new ArgumentNullException(nameof(options));
        _tokenHandler = new JwtSecurityTokenHandler();
        
        // 验证必需的配置
        if (string.IsNullOrEmpty(_options.SecretKey))
        {
            throw new ArgumentException("SecretKey cannot be empty", nameof(_options.SecretKey));
        }
        
        // 创建签名凭据
        var key = Encoding.UTF8.GetBytes(_options.SecretKey);
        _signingCredentials = new SigningCredentials(
            new SymmetricSecurityKey(key),
            SecurityAlgorithms.HmacSha256
        );
    }
    
    /// <summary>
    /// 创建访问令牌
    /// </summary>
    public string CreateAccessToken(string userId, Dictionary<string, object>? claims = null)
    {
        if (string.IsNullOrEmpty(userId))
            throw new ArgumentNullException(nameof(userId));
        
        var issuedAt = DateTime.UtcNow;
        var expiresAt = issuedAt.AddMinutes(_options.AccessTokenExpirationMinutes);
        
        // 创建Claims列表
        var claimsList = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Sub, userId),
            new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(issuedAt).ToUnixTimeSeconds().ToString()),
            new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(expiresAt).ToUnixTimeSeconds().ToString()),
            new Claim(JwtRegisteredClaimNames.Iss, _options.Issuer),
            new Claim(JwtRegisteredClaimNames.Aud, _options.Audience),
            new Claim("token_type", "access")
        };
        
        // 添加自定义声明
        if (claims != null)
        {
            foreach (var claim in claims)
            {
                claimsList.Add(new Claim(claim.Key, claim.Value?.ToString() ?? string.Empty));
            }
        }
        
        // 创建令牌
        var token = new JwtSecurityToken(
            issuer: _options.Issuer,
            audience: _options.Audience,
            claims: claimsList,
            expires: expiresAt,
            signingCredentials: _signingCredentials
        );
        
        return _tokenHandler.WriteToken(token);
    }
    
    /// <summary>
    /// 创建刷新令牌
    /// </summary>
    public string CreateRefreshToken(string userId)
    {
        if (string.IsNullOrEmpty(userId))
            throw new ArgumentNullException(nameof(userId));
        
        // 生成随机字节
        var randomNumber = new byte[32];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(randomNumber);
        }
        
        var tokenId = Convert.ToBase64String(randomNumber);
        var issuedAt = DateTime.UtcNow;
        var expiresAt = issuedAt.AddHours(_options.RefreshTokenExpirationHours);
        
        // 创建刷新令牌Claims
        var claims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Sub, userId),
            new Claim(JwtRegisteredClaimNames.Jti, tokenId),
            new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(issuedAt).ToUnixTimeSeconds().ToString()),
            new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(expiresAt).ToUnixTimeSeconds().ToString()),
            new Claim("token_type", "refresh")
        };
        
        // 创建令牌
        var token = new JwtSecurityToken(
            issuer: _options.Issuer,
            audience: _options.Audience,
            claims: claims,
            expires: expiresAt,
            signingCredentials: _signingCredentials
        );
        
        return _tokenHandler.WriteToken(token);
    }
    
    /// <summary>
    /// 验证访问令牌
    /// </summary>
    public (bool IsValid, string? UserId, Dictionary<string, object>? Claims) ValidateAccessToken(string token)
    {
        if (string.IsNullOrEmpty(token))
            return (false, null, null);
        
        try
        {
            var validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = _options.ValidateToken,
                ValidateAudience = _options.ValidateToken,
                ValidateLifetime = _options.ValidateToken,
                ValidateIssuerSigningKey = true,
                ValidIssuer = _options.Issuer,
                ValidAudience = _options.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.SecretKey)),
                ClockSkew = TimeSpan.FromSeconds(30) // 允许30秒的时钟偏差
            };
            
            ClaimsPrincipal? principal = null;
            SecurityToken? validatedToken = null;
            
            // 验证令牌
            principal = _tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
            
            // 检查令牌类型
            var tokenType = principal.FindFirst("token_type")?.Value;
            if (tokenType != "access")
                return (false, null, null);
            
            // 提取用户ID和自定义声明
            var userId = principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
            var claims = new Dictionary<string, object>();
            
            foreach (var claim in principal.Claims)
            {
                // 排除标准声明
                if (!new[] { JwtRegisteredClaimNames.Sub, JwtRegisteredClaimNames.Iat, JwtRegisteredClaimNames.Exp, 
                             JwtRegisteredClaimNames.Iss, JwtRegisteredClaimNames.Aud, "token_type" }
                    .Contains(claim.Type))
                {
                    claims[claim.Type] = claim.Value;
                }
            }
            
            return (true, userId, claims);
        }
        catch
        {
            return (false, null, null);
        }
    }
    
    /// <summary>
    /// 验证刷新令牌
    /// </summary>
    public (bool IsValid, string? UserId) ValidateRefreshToken(string token)
    {
        if (string.IsNullOrEmpty(token))
            return (false, null);
        
        try
        {
            var validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = _options.ValidateToken,
                ValidateAudience = _options.ValidateToken,
                ValidateLifetime = _options.ValidateToken,
                ValidateIssuerSigningKey = true,
                ValidIssuer = _options.Issuer,
                ValidAudience = _options.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.SecretKey)),
                ClockSkew = TimeSpan.FromSeconds(30)
            };
            
            ClaimsPrincipal? principal = null;
            SecurityToken? validatedToken = null;
            
            principal = _tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
            
            // 检查令牌类型
            var tokenType = principal.FindFirst("token_type")?.Value;
            if (tokenType != "refresh")
                return (false, null);
            
            var userId = principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
            return (true, userId);
        }
        catch
        {
            return (false, null);
        }
    }
    
    /// <summary>
    /// 刷新访问令牌
    /// </summary>
    public string? RefreshAccessToken(string refreshToken)
    {
        // 验证刷新令牌
        var (isValid, userId) = ValidateRefreshToken(refreshToken);
        if (!isValid || string.IsNullOrEmpty(userId))
            return null;
        
        // 创建新的访问令牌
        return CreateAccessToken(userId);
    }
    
    /// <summary>
    /// 检查令牌是否即将过期
    /// </summary>
    public bool IsTokenExpiringSoon(string token, int minutesThreshold = 5)
    {
        if (string.IsNullOrEmpty(token))
            return true;
        
        try
        {
            var securityToken = _tokenHandler.ReadJwtToken(token);
            var expiration = securityToken.ValidTo;
            var timeUntilExpiration = expiration - DateTime.UtcNow;
            
            return timeUntilExpiration.TotalMinutes < minutesThreshold;
        }
        catch
        {
            return true; // 解析失败视为即将过期
        }
    }
}